fix(calibration): don't open cameras until the user turns them on#21
Merged
nicolas-rabault merged 2 commits intoJun 8, 2026
Merged
Conversation
Opening or hard-reloading the calibration page used to switch on the
webcam (and any other connected camera) unprompted. The "Attached
cameras" section mounted on page load and ran getUserMedia immediately:
useAvailableCameras fires a `{ video: true }` permission probe to unlock
device labels, and that probe grabs whatever the OS treats as the default
camera — so a camera could light up even after being removed from the
robot's config, with no user action.
Gate the section behind an explicit On/Off toggle, off by default. While
off, CameraConfiguration is not mounted, so nothing touches a camera; the
panel instead explains that turning cameras on will scan connected
devices and trigger the browser permission prompt. Cameras are only
enumerated and previewed once the user opts in, and toggling off unmounts
the section and releases every stream.
Also add a Rescan button next to the camera list so a newly plugged-in
USB camera can be picked up on demand instead of waiting for the
hotplug auto-refresh.
nicolas-rabault
self-requested a review
June 8, 2026 14:24
nicolas-rabault
approved these changes
Jun 8, 2026
nicolas-rabault
left a comment
Member
There was a problem hiding this comment.
Clean, well-scoped fix and the right diagnosis.
Verified on macOS: nothing activates on load, toggle on scans + previews, toggle off releases, Rescan picks up a hotplug.
cn0303
added a commit
to cn0303/leLab
that referenced
this pull request
Jun 8, 2026
The "Rebuild frontend/dist" workflow ran on Node 18, but the repo moved to Vite 8 (frontend/package.json), which requires Node >=20. Since that bump `npm run build` failed in CI, so the committed frontend/dist silently stopped being regenerated and the shipped bundle fell behind the merged source (e.g. the PR huggingface#21 calibration UI never reached dist, so a plain install/run showed the old UI). Bumping to Node 22 fixes the build. This workflow also triggers on changes to its own file, so merging this re-runs it and rebuilds dist from the current source on the next push to main.
cn0303
added a commit
to cn0303/leLab
that referenced
this pull request
Jun 8, 2026
…ckerfile) A Dependabot PR bumped the repo to Vite 8 (build(deps-dev): bump vite 5→8), which requires Node ^20.19.0 || >=22.12.0. Two build pipelines were still pinned to Node 18, so `npm run build` fails on both: - .github/workflows/build_frontend.yml — the "Rebuild frontend/dist" workflow. Its build failed silently, so the committed frontend/dist stopped being regenerated and the shipped bundle fell behind the merged source (e.g. the PR huggingface#21 calibration UI never reached dist). - frontend/Dockerfile — the image the Hugging Face Space builds. Its `npm run build` step fails, so the Space shows a build error. Bump both to Node 22. The rebuild workflow also triggers on changes to its own file, so merging this re-runs it and regenerates dist from the current source.
nicolas-rabault
pushed a commit
that referenced
this pull request
Jun 9, 2026
…ckerfile) A Dependabot PR bumped the repo to Vite 8 (build(deps-dev): bump vite 5→8), which requires Node ^20.19.0 || >=22.12.0. Two build pipelines were still pinned to Node 18, so `npm run build` fails on both: - .github/workflows/build_frontend.yml — the "Rebuild frontend/dist" workflow. Its build failed silently, so the committed frontend/dist stopped being regenerated and the shipped bundle fell behind the merged source (e.g. the PR #21 calibration UI never reached dist). - frontend/Dockerfile — the image the Hugging Face Space builds. Its `npm run build` step fails, so the Space shows a build error. Bump both to Node 22. The rebuild workflow also triggers on changes to its own file, so merging this re-runs it and regenerates dist from the current source.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Opening or reloading the Calibration page used to switch on the
webcam and any other connected camera without the user doing anything.
This PR gates the camera section behind an explicit On/Off toggle (off by
default), so cameras are only touched once the user opts in.
The bug
The "Attached cameras" section is rendered inline on the calibration page and
mounts on load. On mount,
useAvailableCamerasfires agetUserMedia({ video: true })permission probe to unlock device labels forenumeration. Because
{ video: true }lets the browser pick the device, theprobe grabs whatever the OS treats as the default camera independent of the
robot's config. So:
because the probe doesn't care what's configured.
The fix
Switchto the section header, off by default.CameraConfigurationis not mounted, so nothing callsgetUserMediaand no camera is touched. The panel instead explains thatturning cameras on will scan connected devices and trigger the browser
permission prompt ("Nothing is recorded").
it and releases every stream via the existing hook cleanups.
spins while scanning) so a newly plugged-in USB camera can be picked up on
demand instead of waiting for the
devicechangeauto-refresh. Lives in theshared
CameraConfiguration, so the recording modal gets it too.Before / after
Before UI
After UI
Testing
Frontend only. Verified manually: loaded the calibration page and confirmed no
camera activates until the toggle is switched on; toggling off releases the
streams; Rescan refreshes the list after a hotplug.
npm run buildpasses, andthe changed files pass ESLint directly: